package fm.yichenet.service.impl;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import fm.dao.HibernateBaseDao;
import fm.dao.MongoBaseDao;
import fm.dto.NeiyiUser;
import fm.entity.OrderRepair;
import fm.entity.OrderTransaction;
import fm.entity.WxUser;
import fm.entityEnum.PayStatusEnum;
import fm.entityEnum.RepairEnum;
import fm.exception.BizException;
import fm.mongo.MongoTable;
import fm.nio.SemaphoreExecutor;
import fm.util.CommonUtils;
import fm.web.CurrentRequest;
import fm.yichenet.service.RepairService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang.StringUtils;
import org.hibernate.SQLQuery;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;

/**
 * Created by 宏炜 on 2017-10-24.
 */
@Service
public class RepairServiceImpl implements RepairService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RepairServiceImpl.class);

    @Autowired
    HibernateBaseDao baseDao;

    @Autowired
    MongoBaseDao mongoBaseDao;

    @Override
    public OrderRepair addPublish(String content, String title, String type, String startTime, String endTime, String address, String province, String city, String district, String pics, String phone, String loc) throws Exception {
        OrderRepair orderRepair = new OrderRepair();
        orderRepair.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        orderRepair.setVersion(1L);
        orderRepair.setContent(content);
        orderRepair.setTitle(title);
        orderRepair.setType(type);
        orderRepair.setStartTime(startTime);
        orderRepair.setEndTime(endTime);
        orderRepair.setAddress(address);
        orderRepair.setProvince(province);
        orderRepair.setDistrict(district);
        orderRepair.setCity(city);
        orderRepair.setPhone(phone);
        orderRepair.setStatus(RepairEnum.WITHOUT_CHECK);
        if (!StringUtils.isBlank(loc)) {
            String[] locs = loc.split(",");
            if (locs.length > 1) {
                orderRepair.setLon(new BigDecimal(locs[0]));
                orderRepair.setLat(new BigDecimal(locs[1]));
            }
        }
        orderRepair.setCreateTime(new Timestamp(System.currentTimeMillis()));

        Object user = CurrentRequest.getCurrentLoginUser();

        if (user instanceof WxUser) {
            orderRepair.setPublisherId((String) ((NeiyiUser) user).getId());
        } else {
            throw new BizException("无法获取当前用户信息,发布失败");
        }
        baseDao.save(orderRepair);

        DBObject dbObject = new BasicDBObject();
        dbObject.put("pics", pics.split(","));
        dbObject.put("repairId", orderRepair.getId());
        mongoBaseDao.insert(dbObject.toMap(), MongoTable.order_repair_pic);

        return orderRepair;
    }

    @Override
    public List getEnginemenOrder(NeiyiUser user, Integer pageNum, Integer pageSize, String status, String type) throws InterruptedException {

        String hql = "from OrderRepair where enginemenId = ? and type = ? and status = ?";
        List<OrderRepair> list = (List<OrderRepair>) baseDao.pageQuery(hql, pageNum, pageSize, user.getId(), type, RepairEnum.valueOf(status));
        List<Map<String, Object>> resList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {

            //多线程异步需要同步机制
            final CountDownLatch cdl = new CountDownLatch(list.size());
            SemaphoreExecutor executor = new SemaphoreExecutor(list.size(), "queryOrderRepairPicThreads");

            for (final OrderRepair orderRepair : list) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {

                            Map<String, Object> map = new HashedMap();
                            Map<String, Object> query = new HashedMap();
                            query.put("repairId", orderRepair.getId());
                            DBObject pic = (DBObject) mongoBaseDao.findOne(query, DBObject.class, MongoTable.order_repair_pic);

                            map.put("orderRepair", orderRepair);
                            if (CommonUtils.isEmpty(pic)) {
                                map.put("pics", Collections.EMPTY_LIST);
                            } else {
                                map.put("pics", pic.get("pics"));
                            }

                            resList.add(map);
                        } catch (Exception ex) {
                            LOGGER.error("获取维修订单图片列表发生错误:", ex);
                        } finally {
                            //多线程执行结果等待计数器，-1
                            cdl.countDown();
                        }
                    }
                });
            }

            //等待线程同步结束后再继续后面的代码片段
            cdl.await();
        }
        return resList;
    }

    @Override
    public List getPublishOrder(NeiyiUser user, Integer pageNum, Integer pageSize, String status, String type) throws InterruptedException {
        String hql = "from OrderRepair where publisherId = ? and type = ? and status = ?";
        List<OrderRepair> list = (List<OrderRepair>) baseDao.pageQuery(hql, pageNum, pageSize, user.getId(), type, RepairEnum.valueOf(status));
        List<Map<String, Object>> resList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {

            //多线程异步需要同步机制
            final CountDownLatch cdl = new CountDownLatch(list.size());
            SemaphoreExecutor executor = new SemaphoreExecutor(list.size(), "queryPublishOrderRepairPicThreads");

            for (final OrderRepair orderRepair : list) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {

                            Map<String, Object> map = new HashedMap();
                            Map<String, Object> query = new HashedMap();
                            query.put("repairId", orderRepair.getId());
                            DBObject pic = (DBObject) mongoBaseDao.findOne(query, DBObject.class, MongoTable.order_repair_pic);

                            map.put("orderRepair", orderRepair);
                            if (CommonUtils.isEmpty(pic)) {
                                map.put("pics", Collections.EMPTY_LIST);
                            } else {
                                map.put("pics", pic.get("pics"));
                            }


                            resList.add(map);
                        } catch (Exception ex) {
                            LOGGER.error("获取维修订单图片列表发生错误:", ex);
                        } finally {
                            //多线程执行结果等待计数器，-1
                            cdl.countDown();
                        }
                    }
                });
            }

            //等待线程同步结束后再继续后面的代码片段
            cdl.await();
        }
        return resList;
    }

    @Override
    public void updateCheckRepairOrder(NeiyiUser user, String orderId) throws BizException {


        String hql = "from OrderRepair where status = ? and id = ?";
        OrderRepair orderRepair = (OrderRepair) baseDao.getUnique(hql, RepairEnum.WITHOUT_CHECK, orderId);
        if (CommonUtils.isEmpty(orderRepair)) {
            throw new BizException("未找到订单");
        }
        orderRepair.setStatus(RepairEnum.WAIT_REPAIR);
        orderRepair.setCheckTime(new Timestamp(System.currentTimeMillis()));
        orderRepair.setEnginemenId((String) user.getId());
        baseDao.update(orderRepair);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public OrderRepair getCheckRepairOrder(NeiyiUser user, String orderId) throws BizException {
        String hql = "from OrderRepair where id = ? and enginemenId = ? and status = ?";
        OrderRepair orderRepair = (OrderRepair) baseDao.getUnique(hql, orderId, user.getId(), RepairEnum.WAIT_REPAIR);
        if (CommonUtils.isEmpty(orderRepair)) {
            throw new BizException("抢单失败");
        }
        return orderRepair;
    }

    @Override
    public void updateOrderRepairComplete(NeiyiUser user, String orderId, String amount) throws BizException {
        String hql = "from OrderRepair where id = ? and enginemenId = ? and status = ?";
        OrderRepair orderRepair = (OrderRepair) baseDao.getUnique(hql, orderId, user.getId(), RepairEnum.WAIT_REPAIR);
        if (CommonUtils.isEmpty(orderRepair)) {
            throw new BizException("未找到订单");
        }
        orderRepair.setAmount(new BigDecimal(amount));
        orderRepair.setStatus(RepairEnum.REPAIR_COMPLETE);
        baseDao.update(orderRepair);
    }

    @Override
    public void commentsRepairOrder(NeiyiUser user, String orderId, Integer level, String content, String pics) throws Exception {
        String hql = "from OrderRepair where id = ? and publisherId = ? and status = ?";
        OrderRepair orderRepair = (OrderRepair) baseDao.getUnique(hql, orderId, user.getId(), RepairEnum.PAYMENT_SUCCESS);
        if (CommonUtils.isEmpty(orderRepair)) {
            throw new BizException("未找到订单");
        }
        DBObject obj = new BasicDBObject();
        obj.put("orderId", orderRepair.getId());
        obj.put("content", content);
        obj.put("pics", pics.split(","));
        obj.put("createTime", new Date());

        mongoBaseDao.insert(obj.toMap(), MongoTable.order_repair_comments);
    }

    @Override
    public OrderTransaction addOrderTransaction(String orderId) throws BizException {
        Timestamp now = new Timestamp(System.currentTimeMillis());
        OrderTransaction orderTransaction = new OrderTransaction();
        orderTransaction.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        orderTransaction.setPayStatus(PayStatusEnum.UNPAID);
        orderTransaction.setCreateTime(now);
        orderTransaction.setUpdateTime(now);

        OrderRepair orderRepair = (OrderRepair) baseDao.getById(OrderRepair.class, orderId);

        if (CommonUtils.isEmpty(orderRepair)) {
            throw new BizException("商品订单信息不存在");
        }
        orderRepair.setTransactionOrderId(orderTransaction.getId());
        orderTransaction.setAmount(orderRepair.getAmount().doubleValue());
        orderTransaction.setVersion(1L);
        baseDao.save(orderTransaction);
        return orderTransaction;
    }

    @Override
    public void updateRepairStatus(String orderId, RepairEnum repairEnum) throws BizException {
        String hql = "from OrderRepair where transactionOrderId = ?";
        List<OrderRepair> orderRepairs = (List<OrderRepair>) baseDao.queryForList(hql, orderId);
        if (CommonUtils.isEmpty(orderRepairs)) {
            return;
        }
        Timestamp now = new Timestamp(System.currentTimeMillis());
        for (OrderRepair orderRepair : orderRepairs) {
            switch (repairEnum) {
                case PAYMENT_SUCCESS:
                    if (orderRepair.getStatus().equals(RepairEnum.REPAIR_COMPLETE)) {
                        orderRepair.setStatus(repairEnum);
                        orderRepair.setPayTime(now);
                    }
                    baseDao.save(orderRepair);
                    break;

            }
        }
    }

    @Override
    public List getRepairListByTimeDistance(Double lat, Double lon, String startTime, String province, String city, String district, String type) throws BizException {
        String sql = getRepairQueryString(lat, lon, startTime, province, city, district, type);
        SQLQuery sqlQuery = baseDao.getHibernateTemplate().getSessionFactory().getCurrentSession().createSQLQuery(sql);
        sqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        return sqlQuery.list();
    }

    private String getRepairQueryString(Double lat, Double lon, String startTime, String province, String city, String district, String type) {

        if (lat == null) {
            lat = 0d;
        }
        if (lon == null) {
            lon = 0d;
        }
        double realLat = Math.min(lat, lon);
        double realLng = Math.max(lat, lon);

        DecimalFormat lonDf = new DecimalFormat("#0.000000");
        DecimalFormat latDf = new DecimalFormat("#0.00000");

        StringBuffer sb = new StringBuffer();

        //经纬度查询条件


        sb.append("select * from ( ");
        sb.append("select * from ( ");
        sb.append("select u.*,6378.138 * 2 * ASIN(SQRT(POW(SIN((").append(latDf.format(realLat)).append(" * PI() / 180 - u.lat * PI() / 180) / 2),2)");
        sb.append("+ COS( ").append(latDf.format(realLat)).append(" * PI() / 180) * COS(u.lat * PI() / 180) * POW(SIN((").append(lonDf.format(realLng)).append(" * PI() / 180 ");
        sb.append("- u.lon * PI() / 180 ) / 2 ),2))) AS distance,TIMESTAMPDIFF(MINUTE,create_time,NOW()) as timeDiff ");
        sb.append("from order_repair as u where u.lat is not null and u.lon is not null and status = 'WITHOUT_CHECK' ");

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(startTime)) {
            sb.append(" and u.start_time = '").append(startTime).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(province)) {
            sb.append(" and u.province='").append(province).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(city)) {
            sb.append(" and u.city='").append(city).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(district)) {
            sb.append(" and u.district='").append(district).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(type)) {
            sb.append(" and u.type='").append(type).append("' ");
        }
        sb.append(") a where a.timeDiff < 30 AND a.distance < 2 ");

        sb.append(" union ");

        sb.append("select * from ( ");
        sb.append("select u.*,6378.138 * 2 * ASIN(SQRT(POW(SIN((").append(latDf.format(realLat)).append(" * PI() / 180 - u.lat * PI() / 180) / 2),2)");
        sb.append("+ COS( ").append(latDf.format(realLat)).append(" * PI() / 180) * COS(u.lat * PI() / 180) * POW(SIN((").append(lonDf.format(realLng)).append(" * PI() / 180 ");
        sb.append("- u.lon * PI() / 180 ) / 2 ),2))) AS distance,TIMESTAMPDIFF(MINUTE,create_time,NOW()) as timeDiff ");
        sb.append("from order_repair as u where u.lat is not null and u.lon is not null and status = 'WITHOUT_CHECK'");

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(startTime)) {
            sb.append(" and u.start_time = '").append(startTime).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(province)) {
            sb.append(" and u.province='").append(province).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(city)) {
            sb.append(" and u.city='").append(city).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(district)) {
            sb.append(" and u.district='").append(district).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(type)) {
            sb.append(" and u.type='").append(type).append("' ");
        }
        sb.append(") b where b.timeDiff > 30 AND b.timeDiff < 120 AND b.distance < 20 ");

        sb.append(" union ");

        sb.append("select * from ( ");
        sb.append("select u.*,6378.138 * 2 * ASIN(SQRT(POW(SIN((").append(latDf.format(realLat)).append(" * PI() / 180 - u.lat * PI() / 180) / 2),2)");
        sb.append("+ COS( ").append(latDf.format(realLat)).append(" * PI() / 180) * COS(u.lat * PI() / 180) * POW(SIN((").append(lonDf.format(realLng)).append(" * PI() / 180 ");
        sb.append("- u.lon * PI() / 180 ) / 2 ),2))) AS distance,TIMESTAMPDIFF(MINUTE,create_time,NOW()) as timeDiff ");
        sb.append("from order_repair as u where u.lat is not null and u.lon is not null and status = 'WITHOUT_CHECK'");

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(startTime)) {
            sb.append(" and u.start_time = '").append(startTime).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(province)) {
            sb.append(" and u.province='").append(province).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(city)) {
            sb.append(" and u.city='").append(city).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(district)) {
            sb.append(" and u.district='").append(district).append("' ");
        }

        if (org.apache.commons.lang3.StringUtils.isNotEmpty(type)) {
            sb.append(" and u.type='").append(type).append("' ");
        }
        sb.append(") c where c.timeDiff > 120");
        sb.append(") d ORDER BY d.create_time desc");

        return sb.toString();

    }
}
